/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.huawei.health.ecology.fa.central.processor;

import com.huawei.health.ecology.fa.central.api.OhosPermissionOperation;
import com.huawei.health.ecology.fa.central.callback.PermissionStateCallback;
import com.huawei.health.ecology.fa.central.data.PermissionRequestResult;
import com.huawei.health.ecology.fa.central.data.PermissionState;
import com.huawei.health.ecology.fa.central.request.PermissionAcquireRequest;
import com.huawei.health.ecology.fa.central.request.PermissionVerifyRequest;
import com.huawei.health.ecology.fa.central.response.CentralResponseCode;
import com.huawei.health.ecology.fa.central.response.PermissionStateResponse;
import com.huawei.health.ecology.fa.utils.LogUtil;
import com.huawei.healthecology.data.utils.CallbackProvider;
import com.huawei.healthecology.data.utils.OptionalX;
import com.huawei.healthecology.processor.HealthEcologyProcessor;

import lombok.Builder;
import lombok.Setter;
import ohos.app.Context;
import ohos.bundle.IBundleManager;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * The processor for permission check and request
 */
@Builder
public class OhosPermissionProcessor implements OhosPermissionOperation, HealthEcologyProcessor {
    private static final String TAG = "PermissionProcessor";

    private static final int PERMISSION_REQUEST_CODE = 1000;

    private final Context context;

    @Setter
    private CallbackProvider<PermissionStateCallback> permissionStateProvider;

    @Override
    public void initProcessor() {
    }

    @Override
    public void releaseResource() {
    }

    @Override
    public void destroyProcessor() {
        this.clearAllCallback();
    }

    /**
     * clear all callback
     */
    public void clearAllCallback() {
        Optional.ofNullable(permissionStateProvider).ifPresent(CallbackProvider::clear);
    }

    @Override
    public PermissionStateResponse checkPermissions(PermissionVerifyRequest permissionVerifyRequest) {
        List<String> permissions = permissionVerifyRequest.getPermissions();
        List<String> canRequestPermissions = permissions.stream()
            .filter(permission -> context.verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED)
            .filter(context::canRequestPermission)
            .collect(Collectors.toList());
        List<String> grantedPermissions = permissions.stream()
            .filter(permission -> context.verifySelfPermission(permission) == IBundleManager.PERMISSION_GRANTED)
            .collect(Collectors.toList());
        LogUtil.debug(TAG, "canRequestPermissions : " + canRequestPermissions);
        PermissionState permissionState = PermissionState.builder()
            .requestedPermissions(permissions)
            .grantedPermissions(grantedPermissions)
            .availablePermissions(canRequestPermissions)
            .build();
        return PermissionStateResponse.builder()
            .centralResponseCode(CentralResponseCode.OPERATION_SUCCESS)
            .permissionState(permissionState)
            .build();
    }

    @Override
    public CentralResponseCode requestPermissions(PermissionAcquireRequest permissionAcquireRequest) {
        Optional.of(permissionAcquireRequest.getPermissions()).ifPresent(permissions -> {
            String[] permissionsArray = permissions.toArray(new String[0]);
            context.requestPermissionsFromUser(permissionsArray, PERMISSION_REQUEST_CODE);
        });
        return CentralResponseCode.OPERATION_SUCCESS;
    }

    @Override
    public void onPermissionRequest(PermissionStateCallback permissionStateCallback) {
        OptionalX.ofNullable(permissionStateProvider)
            .ifPresent(callbackProvider -> callbackProvider.add(permissionStateCallback))
            .ifNotPresent(() -> permissionStateProvider =
                CallbackProvider.<PermissionStateCallback> builder().callback(permissionStateCallback).build());
    }

    /**
     * Permission grant callback, parses whether to grant location permission, and invokes the authorization callback
     * to notify the upper layer.
     *
     * @param requestCode request code
     * @param permissions Permissions to be applied for
     * @param grantResults granted permissions
     */
    public void onPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
        if (permissions == null || grantResults == null) {
            return;
        }
        List<String> permissionGranted = IntStream.range(0, permissions.length)
            .filter(index -> grantResults[index] == IBundleManager.PERMISSION_GRANTED)
            .mapToObj(index -> permissions[index])
            .collect(Collectors.toList());
        List<String> availablePermissions = IntStream.range(0, permissions.length)
            .filter(index -> grantResults[index] != IBundleManager.PERMISSION_GRANTED)
            .filter(index -> context.canRequestPermission(permissions[index]))
            .mapToObj(index -> permissions[index])
            .collect(Collectors.toList());
        PermissionStateCallback permissionStateCallback = Optional.ofNullable(permissionStateProvider)
            .flatMap(CallbackProvider::getLast)
            .orElse(null);
        PermissionState permissionState = PermissionState.builder()
            .requestedPermissions(Arrays.asList(permissions))
            .grantedPermissions(permissionGranted)
            .availablePermissions(availablePermissions)
            .build();
        OptionalX.ofNullable(permissionStateCallback)
            .ifPresent(
                callback -> callback.onPermissionResult(PermissionRequestResult.of(requestCode, permissionState)))
            .ifNotPresent(() -> LogUtil.debug(TAG, "permissionStateCallback is empty"));
    }
}
